package cn.taobaoke.restful.service;

import cn.taobaoke.restful.domain.*;
import cn.taobaoke.restful.exception.TBKException;
import cn.taobaoke.restful.util.Constants;
import cn.taobaoke.restful.util.ExcelWorkbookFactory;
import cn.taobaoke.restful.util.MD5Util;
import cn.taobaoke.restful.util.TokenGenerator;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 代理服务层.
 *
 * @author yangzuo
 * @since 2017-03-11
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class AgentService {

    /**
     * 判断代理是否存在.
     */
    public AgentEntity checkAgentExistById(final Long agentId, final Long domainId) {
        val agentOpt = agentRepository.findByIdAndDomainId(agentId, domainId);
        if (! agentOpt.isPresent()) {
            logger.error("代理用户ID [{}] 不存在", agentId);
            throw new TBKException("代理用户ID [" + agentId + "] 不存在");
        }
        return agentOpt.get();
    }

    /**
     * 代理登录用户查询
     */
    public AgentEntity loginByKeywordAndPassword(final String name, final String password, final Long domainId) {
        String md5Pwd = MD5Util.md5(password);
        val agentOpt = agentRepository.findByKeywordAndPassword(name, md5Pwd, domainId);
        return agentOpt.orElse(null);
    }

    /**
     * 新增代理
     */
    public AgentEntity insertAgentEntity(AgentEntity agentEntity, final Long domainId) {

        Optional<AgentEntity> entityOpt = agentRepository.findByWeChat(agentEntity.getWeChat(), domainId);
        if (entityOpt.isPresent()) {
            logger.error("代理账号 [{}] 已经存在", agentEntity.getWeChat());
            throw new TBKException("微信号 [" + agentEntity.getWeChat() + "] 已经存在");
        }
        entityOpt = agentRepository.findByName(agentEntity.getName(), domainId);
        if (entityOpt.isPresent()) {
            logger.error("代理账号用户名 [{}] 已经存在", agentEntity.getName());
            throw new TBKException("代理账号用户名 [" + agentEntity.getName() + "] 已经存在");
        }

        agentEntity.setId(null);
        String md5Pwd = MD5Util.md5(agentEntity.getPassword());
        agentEntity.setPassword(md5Pwd);
        agentEntity.setCreateTime(new Date());
        agentEntity.setToken(TokenGenerator.generateToken(agentEntity.getWeChat() + agentEntity.getName()));
        agentEntity.setIsDelete(false);
        agentEntity.setDomainId(domainId);

        if (agentEntity.getParentId() == null) {
            agentEntity.setLevel(1);
        } else {
            val parentOpt = agentRepository.findByIdAndDomainId(agentEntity.getParentId(), domainId);
            if (! parentOpt.isPresent()) {
                throw new TBKException("不存在的parentId => " + agentEntity.getParentId());
            }
            AgentEntity parent = parentOpt.get();
            int parentLevel = parent.getLevel() == null ? 1 : parent.getLevel();
            agentEntity.setLevel(parentLevel + 1);
            agentEntity.setGrandParentId(parent.getParentId());
        }

        // @add by luc 设置默认的分佣比例 2017/8/16
        if (agentEntity.getPerOfCommission() == null) {
            // 设置佣金
            // notice, we use default commission as ParentOfCommission and GrandParentPerOfCommission.
            Map<String, Object> config = globalConfigService.queryConfig(domainId);

            Double perOfCommission = Double.parseDouble(config.get(Constants.PROMOTER_PER_OF_COMMISSION).toString());
            Double parentPerOfCommission = Double.parseDouble(config.get(Constants.CLASS1_PER_OF_COMMISSION).toString());
            Double grandParentOfCommission = Double.parseDouble(config.get(Constants.CLASS2_PER_OF_COMMISSION).toString());
            agentEntity.setPerOfCommission(perOfCommission);
            agentEntity.setParentPerOfCommission(parentPerOfCommission);
            agentEntity.setGrandParentPerOfCommission(grandParentOfCommission);
        }
        // @end

        AgentEntity retEntity = agentRepository.save(agentEntity);

        List<String> groupNames = agentEntity.getGroupNames();
        if (groupNames != null) {
            groupNames.forEach(item -> buildRelationBtwAgentAndGroup(retEntity.getId(), cleanGroupName(item), domainId));
        }

        logger.info("新增代理, ID: {}, WeChat: {}", retEntity.getId(), retEntity.getWeChat());
        return retEntity;
    }

    private String cleanGroupName(String groupName) {
        return groupName.replace(",", "").replace(",", "").trim();
    }

    /**
     * 根据Id删除代理, 包括其下面的子代理进行删除
     */
    public void deleteAgentEntity(final Long agentId, final Long domainId) {
        // TODO 这里三处有问题,三级之后的没有删除呢?

        agentRepository.deleteByIdOrParentIdOrGrandParentId(agentId, domainId);
        logger.warn("删除代理账号[{}]", agentId);
    }

    /**
     * 更新代理信息
     */
    public AgentEntity updateAgentEntity(AgentEntity agentEntity, final Long domainId) {
        Long agentId = agentEntity.getId();
        val entityOpt = agentRepository.findByIdAndDomainId(agentEntity.getId(), domainId);
        if (! entityOpt.isPresent()) {
            logger.error("不存在的代理ID [{}]", agentId);
            throw new TBKException("不存在的代理ID [" + agentId + "]");
        }

        logger.info("update agentEntity => {}", agentEntity);

        val entity = entityOpt.get();
        if (agentEntity.getPassword() != null) {
            entity.setPassword(MD5Util.md5(agentEntity.getPassword()));
        }
        if (agentEntity.getWeChat() != null) {
            entity.setWeChat(agentEntity.getWeChat());
        }
        if (agentEntity.getName() != null) {
            entity.setName(agentEntity.getName());
        }
        if (agentEntity.getNickName() != null) {
            entity.setNickName(agentEntity.getNickName());
        }
        if (agentEntity.getPhone() != null) {
            entity.setPhone(agentEntity.getPhone());
        }
        if (agentEntity.getParentId() != null) {
            entity.setParentId(agentEntity.getParentId());
        }
        if (agentEntity.getPerOfCommission() != null) {
            entity.setPerOfCommission(agentEntity.getPerOfCommission());
        }
        if (agentEntity.getGrandParentPerOfCommission() != null) {
            entity.setGrandParentPerOfCommission(agentEntity.getGrandParentPerOfCommission());
        }
        if (agentEntity.getAlipay() != null) {
            entity.setAlipay(agentEntity.getAlipay());
        }

        // 说明PID被替换了
        if (agentEntity.getPid() != null && entity.getPid() != null && !entity.getPid().equals(agentEntity.getPid())) {
            String newPid = agentEntity.getPid();
            String oldPid = entity.getPid();
            entity.setPid(newPid);
            pidService.saveOrCreatePid(newPid, "mannual", entity.getId(), oldPid, domainId);
        }

        if (agentEntity.getParentId() == null) {
            entity.setLevel(1);
        } else {
            val parentOpt = agentRepository.findByIdAndDomainId(agentEntity.getParentId(), domainId);
            if (! parentOpt.isPresent()) {
                throw new TBKException("不存在的parentId => " + agentEntity.getParentId());
            }
            val parent = parentOpt.get();
            int parentLevel = parent.getLevel() == null ? 1 : parent.getLevel();
            entity.setLevel(parentLevel + 1);
            entity.setGrandParentId(parent.getParentId());
        }

        logger.info("update db entity => {}", entity);

        if (agentEntity.getGroupNames() != null && agentEntity.getGroupNames().size() > 0) {
            final Long ownerId = entity.getId();
            List<String> groupNames = agentEntity.getGroupNames();
            List<AgentGroupRelation> agentGroupRelations = agentGroupRelationRepository.findByAgentIdAndDomainId(ownerId, domainId);
            if (agentGroupRelations != null && agentGroupRelations.size() > 0) {
                agentGroupRelationRepository.deleteAll(agentGroupRelations);
            }
            groupNames.forEach(item -> buildRelationBtwAgentAndGroup(ownerId, cleanGroupName(item), domainId));
            entity.setGroupNames(groupNames);
        }
        entity.setModifiedTime(new Date());
        val result = agentRepository.save(entity);
        logger.info("修改代理账号[{}]信息", entity.getId());
        return filterPassword(result);
    }


    /**
     * agent create or save sub agent info.
     *
     * @param agentId
     * @param subAgent
     * @return
     */
    public AgentEntity saveAgentSubAgent(final Long agentId, final AgentEntity subAgent, final Long domainId) {
        subAgent.setParentId(agentId);

        if (subAgent.getId() == null) {
            // notice, we use default commission as ParentOfCommission and GrandParentPerOfCommission.
            Map<String, Object> config = globalConfigService.queryConfig(domainId);

            Double perOfCommission = Double.parseDouble(config.get(Constants.PROMOTER_PER_OF_COMMISSION).toString());
            Double parentPerOfCommission = Double.parseDouble(config.get(Constants.CLASS1_PER_OF_COMMISSION).toString());
            Double grandParentOfCommission = Double.parseDouble(config.get(Constants.CLASS2_PER_OF_COMMISSION).toString());
            subAgent.setPerOfCommission(perOfCommission);
            subAgent.setParentPerOfCommission(parentPerOfCommission);
            subAgent.setGrandParentPerOfCommission(grandParentOfCommission);

            // create sub agent info.
            PIDEntity pidEntity = pidService.getFirstAvailablePID(domainId);
            subAgent.setPid(pidEntity.getPid());
            pidEntity.setIsAssigned(true);

            val result = insertAgentEntity(subAgent, domainId);

            pidEntity.setAgentId(result.getId());
            pidService.updatePID(pidEntity);

            return result;
        } else {
            val agentOpt = agentRepository.findByIdAndDomainId(agentId, domainId);
            if (! agentOpt.isPresent()) {
                throw new TBKException("不存在的AgentId => " + agentId);
            }
            val agent = agentOpt.get();
            Integer level = agent.getLevel();
            if (level == null) level = 1;
            subAgent.setLevel(level + 1);

            return updateAgentEntity(subAgent, domainId);
        }
    }

    public AgentEntity findAgentByTokenAndDomainId(final String token, final Long domainId) {
        return agentRepository.findByToken(token, domainId).orElse(null);
    }

    /**
     * 通过生成的Token查询AgentEntity
     */
    public AgentEntity getAgentByTokenAndDomainId(final String token, final Long domainId) {
        val agentOptional = agentRepository.findByToken(token, domainId);
        if (! agentOptional.isPresent()) {
            logger.error("不存在的Token [{}]", token);
            throw new TBKException("不存在的Token [" + token + "]");
        }
        return agentOptional.get();
    }

    /**
     * @param agentId
     * @return
     */
    public AgentEntity getAgentById(final Long agentId, final Long domainId) {
        val agentOpt = agentRepository.findByIdAndDomainId(agentId, domainId);
        if (!agentOpt.isPresent()) {
            logger.error("不存在的代理ID [{}]", agentId);
            throw new TBKException("不存在的代理ID [" + agentId + "]");
        }
        val agentEntity = agentOpt.get();
        agentEntity.setGroupNames(getGroupNamesForAgent(agentId, domainId));
        return agentEntity;
    }

    public List<AgentEntity> getAgentByIdIn(final Collection<Long> agentIds, final Long domainId) {
        return agentRepository.findByIdIn(agentIds, domainId);
    }

    /**
     * 查询所有的代理
     */
    public Page<AgentEntity> searchForAgent(String keyword, Long agentId, Pageable pageable, final Long domainId) {
        keyword = keyword == null ? "" : keyword;
        Page<AgentEntity> agentEntityList;
        if (agentId == null) {
            agentEntityList = agentRepository.findByKeyword(keyword, domainId, pageable);
        } else {
            agentEntityList = agentRepository.findByKeywordAndParentId(keyword, agentId, domainId, pageable);
        }
        logger.info("result size => {}", agentEntityList.getTotalElements());
        agentEntityList.getContent().forEach(f -> f.setGroupNames(getGroupNamesForAgent(f.getId(), domainId)));
        filterPassword(agentEntityList.getContent());
        return agentEntityList;
    }

    public Page<AgentEntity> search(String keyword, String type, Pageable pageable, final Long domainId) {
        keyword = keyword == null ? "" : keyword;
        Page<AgentEntity> agentEntityList;
        if (type == null) {
            agentEntityList = agentRepository.findByKeyword(keyword, domainId, pageable);
        } else {
            agentEntityList = agentRepository.findByKeywordAndType(keyword, type, domainId, pageable);
        }

        agentEntityList.getContent().forEach(f -> f.setGroupNames(getGroupNamesForAgent(f.getId(), domainId)));
        filterPassword(agentEntityList.getContent());
        return agentEntityList;
    }


    public List<AgentEntity> addAgent2ParentAgent(final List<AgentEntity> agents, final Long domainId) {
        if (agents == null || agents.isEmpty()) return Collections.EMPTY_LIST;

        final HashSet<Long> agent1Ids = new HashSet<>();
        final List<AgentEntity> agent2s = new ArrayList<>();
        agents.forEach(agent -> {
            if (AgentEntity.PROXY_TYPE_CLASS2.equals(agent.getType())) {
                if (agent.getParentId() != null) {
                    agent1Ids.add(agent.getParentId());
                    agent2s.add(agent);
                }
            }
        });

        if (agent1Ids.isEmpty()) return agents;

        List<AgentEntity> parents = agentRepository.findByIdIn(agent1Ids, domainId);

        for (AgentEntity agent2 : agent2s) {
            for (AgentEntity parent : parents) {
                if (agent2.getParentId().longValue() == parent.getId().longValue()) {
                    agent2.setParent(parent);
                    break;
                }
            }
        }
        return agents;
    }

    public Page<AgentEntity> getDescendantByPage(Long ancestorId, String keyword, Pageable pageable, final Long domainId) {
        keyword = keyword == null ? "" : keyword;
        if (ancestorId == null) {
            return agentRepository.findByKeyword(keyword, domainId, pageable);
        } else {
            return agentRepository.findByKeywordAndParentIdOrGrandParentId(ancestorId, keyword, domainId, pageable);
        }
    }


    public List<AgentEntity> getAllAgents(final Long domainId) {
        List<AgentEntity> agents = agentRepository.findAll(domainId);
        return filterPassword(agents);
    }

    public List<AgentEntity> getAllChildren(final Long agentId, final Long domainId) {
        logger.info("getAllChildren agentId => {}", agentId);
        if (agentId == null) {
            return agentRepository.findByParentIdNull(domainId);
        } else {
            return agentRepository.findByParentId(agentId, domainId);
        }
    }

    public Page<AgentEntity> searchChildren(Long parentId, String keyword, Pageable pageable, final Long domainId) {
        keyword = keyword == null ? "" : keyword;
        return agentRepository.findByKeywordAndParentId(parentId, keyword, domainId, pageable);
    }

    /**
     * 根据代理id查询代理的组信息
     */
    private List<String> getGroupNamesForAgent(final Long agentId, final Long domainId) {

        val wxGroupEntityList = wxGroupRepository.findGroupsForAgent(agentId, domainId);
        val result = new ArrayList<String>();
        for (WxGroupEntity entity : wxGroupEntityList) {
            result.add(entity.getName());
        }
        return result;
//
//        return wxGroupEntityList.stream().map(f -> f.getName()).collect(Collectors.toList());
    }


    /**
     * @param agentId
     * @param groupName
     */
    private void buildRelationBtwAgentAndGroup(final Long agentId, String groupName, final Long domainId) {
        groupName = cleanGroupName(groupName);
        val groupEntityOpt = wxGroupRepository.findFirstByNameAndDomainId(groupName, domainId);
        val agentEntityOpt = agentRepository.findByIdAndDomainId(agentId, domainId);
        if (agentEntityOpt.isPresent()) {
            if (! groupEntityOpt.isPresent()) {
                WxGroupEntity entity = new WxGroupEntity();
                entity.setName(groupName);
                entity.setIsBelongToAdmin(false);
                entity.setAgentId(agentId);
                entity.setDomainId(domainId);
                entity = wxGroupRepository.save(entity);

                AgentGroupRelation relation = new AgentGroupRelation();
                relation.setAgentId(agentId);
                relation.setGroupId(entity.getId());
                relation.setDomainId(domainId);
                agentGroupRelationRepository.save(relation);
            } else {
                val groupEntity = groupEntityOpt.get();
                val relationOpt = agentGroupRelationRepository.findByAgentIdAndGroupIdAndDomainId(agentId, groupEntity.getId(), domainId);
                val relation = relationOpt.orElseGet(AgentGroupRelation::new);
                relation.setAgentId(agentId);
                relation.setGroupId(groupEntity.getId());
                relation.setDomainId(domainId);
                agentGroupRelationRepository.save(relation);
                groupEntity.setAgentId(agentId);
                groupEntity.setDomainId(domainId);
                wxGroupRepository.save(groupEntity);
            }
        }
    }

    /**
     * 过滤敏感信息
     */
    public AgentEntity filterPassword(AgentEntity agentEntity) {
        AgentEntity dto = new AgentEntity();
        dto.setId(agentEntity.getId());
        dto.setName(agentEntity.getName());
        dto.setWeChat(agentEntity.getWeChat());
        dto.setPhone(agentEntity.getPhone());
        dto.setPerOfCommission(agentEntity.getPerOfCommission());
        dto.setClass1PerOfCommission(agentEntity.getClass1PerOfCommission());
        dto.setParentId(agentEntity.getParentId());
        dto.setPid(agentEntity.getPid());
        dto.setAlipay(agentEntity.getAlipay());
        dto.setType(agentEntity.getType());
        dto.setCreateTime(agentEntity.getCreateTime());
        dto.setModifiedTime(agentEntity.getModifiedTime());
        dto.setGroupNames(agentEntity.getGroupNames());
        return dto;
    }

    private List<AgentEntity> filterPassword(List<AgentEntity> list) {
        List<AgentEntity> filterList = new ArrayList<>();
        for (AgentEntity entity : list) {
            filterList.add(filterPassword(entity));
        }
        return filterList;
    }

    public Map<String, String> batchCreateAgents(InputStream inputStream, String filename, final Long domainId) throws IOException {

        Map<String, String> result = new HashMap<>();

        try (Workbook workbook = ExcelWorkbookFactory.create(inputStream, filename)) {
            Sheet sheet = workbook.getSheetAt(0); // 至少存在一个sheet页, 采用默认第一页.
            int start = sheet.getFirstRowNum();
            int end = sheet.getLastRowNum();

            boolean hasEnoughPid = true;
            for (int i = start+1; i <= end; i++) {
                Row row = sheet.getRow(i);
                Cell agentCell = row.getCell(0);
                Cell ratioCell = row.getCell(1);
                Cell pidCell = row.getCell(2);

                if (agentCell == null || "".equals(StringUtils.trimWhitespace(agentCell.toString()))) continue;
                String agent = agentCell.toString();

                Double perOfCommission;
                if (ratioCell == null) {
                    perOfCommission = 45.0;
                } else {
                    String tmp = StringUtils.trimWhitespace(ratioCell.toString());
                    if (tmp.length() == 0) {
                        perOfCommission = 45.0;
                    } else {
                        perOfCommission = Double.valueOf(tmp);
                    }
                }

//                perOfCommission = StringUtils.isNullOrEmpty(ratioCell) ? 45.0 : Double.valueOf(ratioCell.toString());

                String pid = null;
                if (pidCell != null) {
                    pid = pidCell.toString();
                    AgentEntity agentEntity = new AgentEntity();
                    agentEntity.setPid(pid);
                    agentEntity.setPerOfCommission(perOfCommission);
                    agentEntity.setPassword("112233");
                    agentEntity.setName(agent);
                    try {
                        agentEntity = insertAgentEntity(agentEntity, domainId);
                        pidService.savePid(pid, "manual", agentEntity.getId());
                        result.put(agent, "导入成功");
                    } catch (TBKException e) {
                        result.put(agent, "导入失败, " + e.getMessage());
                    }

                } else {

                    if (!hasEnoughPid) {
                        result.put(agent, "导入失败，没有可用的PID");
                        continue;
                    }

                    try {
                        PIDEntity pidEntity = pidService.getFirstAvailablePID(domainId);
                        pid = pidEntity.getPid();
                        AgentEntity agentEntity = new AgentEntity();
                        agentEntity.setPid(pid);
                        agentEntity.setPerOfCommission(perOfCommission);
                        agentEntity.setPassword("112233");
                        agentEntity.setName(agent);
                        agentEntity = insertAgentEntity(agentEntity, domainId);
                        pidEntity.setAgentId(agentEntity.getId());
                        pidEntity.setIsAssigned(true);
                        pidService.updatePID(pidEntity);
                        result.put(agent, "导入成功");
                    } catch (TBKException e) {
                        result.put(agent, "导入失败, " + e.getMessage());
                        hasEnoughPid = false;
                    }

                }
            }
        }

        return result;
    }

    private final @NonNull GlobalConfigService globalConfigService;

    private final @NonNull PIDService pidService;

    private final @NonNull AgentRepository agentRepository;

    private final @NonNull WxGroupRepository wxGroupRepository;

    private final @NonNull AgentGroupRelationRepository agentGroupRelationRepository;

    private static Logger logger = LogManager.getLogger(AgentService.class);

}
